<template>
  <div style="position: relative;">
    <div v-if="showSelected?props.option.selected:!props.option.selected" v-for="(l,index) of lines" :key="index"
         class="line"
         v-bind:class='{selected:props.option.selected}'
         :style='{"top":l.y*zoom+"px","left":l.x*zoom+"px","width":l.w*zoom+"px","height":l.h+"px",
       "transform":"rotate("+l.a+"deg) translateY(-50%)",
       "background-color":props.option.fillColor,
       "border-color":props.option.borderColor,
       "border-width":props.option.borderWidth+"px",
       pointerEvents:props.option.isDraggingPoint?"none":"auto"}'
         @click="handleLineClick($event,props.option)" @dblclick="handleLineDbClick($event,l,index)">
      <div v-if="l.isLast" style="width:0px;height:0px;position: absolute;left: 100%;top:0%;"
           :style='{"color":props.option.fillColor,}'>
        <m-arrow v-if="props.option.hasArrow" :size="l.h*zoom"></m-arrow>
      </div>
      <div v-if="l.isFirst">
        {{ props.option.option.label }}
      </div>
    </div>
    <!--转折处覆盖的圆-->
<!--    <div v-if="showSelected?props.option.selected:!props.option.selected" v-for="(point,index) of props.option.points"-->
<!--         class="circle"-->
<!--         :key="index"-->
<!--         :style='{left:point.x*zoom+"px",top:point.y*zoom+"px",width:props.option.width+"px",height:props.option.width+"px",-->
<!--         "background-color":props.option.fillColor,-->
<!--       "border-color":props.option.borderColor,-->
<!--       "border-width":props.option.borderWidth+"px",}'-->
<!--         @click="handleLineClick($event,props.option)">-->
<!--    </div>-->
    <!--折点-->
    <div v-if="showSelected && props.option.selected" v-for="(point,index) of props.option.points" class="point"
         :key="index"
         :style='{left:point.x*zoom+"px",top:point.y*zoom+"px",
         pointerEvents:props.option.isDraggingPoint?"none":"auto"}' @dragstart="$event.preventDefault()"
         @mousedown="handlePointMouseDown($event,point,index===0,(index===props.option.points.length-1))"
         @click="handleLineClick($event,props.option)" @dblclick="handlePointClick($event,point)">
    </div>
  </div>
</template>
<script setup lang="ts">
import {LinkOption} from "../../LinkOption.ts";
import {nextTick, onMounted, Ref, ref, watch} from "vue";
import {EPoint} from "../../EPoint.ts";
import {ComponentAnchor} from "../../ComponentOption.ts";
import MArrow from "../../MArrow.vue";

/**
 * 连接线的vue组件，从开始到结束，连接其中每个点，并且提供用户交互
 */
const emit = defineEmits(['select', 'dragLinePoint'])
defineExpose({sync})
const lines: Ref<any> = ref([])
const props = defineProps<{
  option: LinkOption,//线的配置
  showSelected: boolean,//线是否被选中
  zoom: number,//放大系数
}>();
//检测到线的属性有变化，就重新把折点换成多条直线
watch(() => props.option, () => {
  sync();
}, {deep: true})
onMounted(() => {
  sync();
})

function sync() {
  //把点转成直线
  let points = props.option.points;
  if (props.option.points.length > 1) {
    let newLines: Array<any> = []
    for (let index = 0; index < points.length - 1; index++) {
      let p1 = points[index];
      let p2 = points[index + 1];
      //把两个点排个序
      // let pa = sort(p1, p2)
      // p1 = pa[0]
      // p2 = pa[1]

      //如果线要自动垂直和水平，就在点与点之间渲染两条直线
      if (props.option.autoHV) {
        //计算点与点之间构成的直线布局
        //如果一个点关联到的是一个锚点，需要知道这个锚点相对于其组件的位置，根据此位置可以让连接线伸出一部分
        //查看第一个点关联到的锚点
        let anchor1 = getPointAnchor(p1);
        //得到第二个点关联到的锚点
        let anchor2 = getPointAnchor(p2);
        let np1: EPoint | null = null
        let np2: EPoint | null = null
        if (anchor1) {
          //第一个点关联到了锚点
          np1 = getAnchorExtendPoint(anchor1, p1);
        }
        if (anchor2) {
          //第二个点关联到了锚点
          np2 = getAnchorExtendPoint(anchor2, p2);
        }
        if (np1 && !np2) {
          createLine(newLines, p1, np1, index)
          createFoldLine(newLines, np1, p2, index)
        } else if (np1 && np2) {
          createLine(newLines, p1, np1, index)
          createFoldLine(newLines, np1, np2, index)
          createLine(newLines, np2, p2, index)
        } else if (!np1 && np2) {
          createFoldLine(newLines, p1, np2, index)
          createLine(newLines, np2, p2, index)
        } else {
          //得到中间点
          createFoldLine(newLines, p1, p2, index)
        }
      } else {
        createLine(newLines, p1, p2, index)
      }
    }
    lines.value = newLines

    //在最后一个点的地方渲染一个箭头
    if (newLines.length > 0) {
      let firstLine: any = newLines[0];
      firstLine.isFirst = true
      let lastLine: any = newLines[newLines.length - 1];
      lastLine.isLast = true;
    }
  }
  // console.log(lines.value)
}

//创建两个点之间的垂直水平折线
//TODO:如果都是两个点之间都是水平或垂直，就从中心点转折，如果是不同的方向，就找一个转弯最小的
function createFoldLine(lines: Array<any>, p1: EPoint, p2: EPoint, index: number) {
  // let pa = sortY(p1, p2)
  // p1 = pa[0]
  // p2 = pa[1]
  //根据两个锚点指出的方向，判断选择哪个中间点
  //如果是垂直的就创建一条垂直的线
  if (p1.x === p2.x || p1.y === p2.y) {
    createLine(lines, p1, p2, index);
  } else {
    //寻找拐点，优先根据上一条线的方向确定。没有思路，放弃
    let np: EPoint = new EPoint(p1.x, p2.y);
    createLine(lines, p1, np, index);
    createLine(lines, np, p2, index);
  }
}

//创建两个点之间的垂直水平折线，从中心点分开
function createFoldLineCrossCenter(lines: Array<any>, p1: EPoint, p2: EPoint, index: number) {
  //找到长方形是横长还是纵长
  let deltaX = Math.abs(p1.x - p2.x);
  let deltaY = Math.abs(p1.y - p2.y);
  let centerX = p1.x + (p2.x - p1.x) / 2
  let centerY = p1.y + (p2.y - p1.y) / 2
  if (deltaX >= deltaY) {
    //横长
    let np1: EPoint = new EPoint(centerX, p1.y)
    let np2: EPoint = new EPoint(centerX, p2.y)
    createLine(lines, p1, np1, index);
    createLine(lines, np1, np2, index);
    createLine(lines, np2, p2, index);
  } else {
    //纵长
    let np1: EPoint = new EPoint(p1.x, centerY)
    let np2: EPoint = new EPoint(p2.x, centerY)
    createLine(lines, p1, np1, index);
    createLine(lines, np1, np2, index);
    createLine(lines, np2, p2, index);
  }
}

//创建一条直线
function createLine(lines: Array<any>, p1: EPoint, p2: EPoint, index: number) {
  // let pa = sort(p1, p2)
  // let np1 = pa[0]
  // let np2 = pa[1]
  let np1 = p1
  let np2 = p2
  let {distance, a} = figure(np1, np2)
  lines.push({
    x: np1.x,
    y: np1.y,
    x1: np2.x,
    y1: np2.y,
    endPoint: p2,//终点
    startPoint: p1,//起点
    w: distance,
    h: props.option.width,
    a: a,
    index: index
  });
}

//找到点关联的锚点
function getPointAnchor(point: EPoint): ComponentAnchor {
  if (props.option.startAnchor && props.option.startAnchor.point.x === point.x && props.option.startAnchor.point.y === point.y) {
    return props.option.startAnchor;
  }
  if (props.option.endAnchor && props.option.endAnchor.point.x === point.x && props.option.endAnchor.point.y === point.y) {
    return props.option.endAnchor;
  }
}

//找到一个锚点延伸出来的点，当前延伸出20像素
function getAnchorExtendPoint(anchor: ComponentAnchor, point: EPoint): EPoint | null | undefined {
  //只有在边界上的锚点才需要延伸
  let p = point.copy();
  if (anchor.px === 1 || anchor.py === 1 || anchor.px === 0 || anchor.py === 0) {
    if (anchor.px === 0) {
      //左边
      p.x = p.x - 20;
      return p
    }
    if (anchor.px === 1) {
      //右边
      p.x = p.x + 20;
      return p
    }
    if (anchor.py === 0) {
      //上边
      p.y = p.y - 20;
      return p
    }
    if (anchor.py === 1) {
      //下边
      p.y = p.y + 20;
      return p;
    }
  } else {
    return null;
  }
}

// 计算两个点的距离 和 偏移角度
function figure(p1: EPoint, p2: EPoint) {
  //计算两个点的距离 和 偏移角度
  let distance = Math.sqrt(Math.pow((p2.y - p1.y), 2) + Math.pow((p2.x - p1.x), 2))
  let deltaY = p2.y - p1.y
  let deltaX = p2.x - p1.x
  let cos = (deltaX) / distance;
  let a = Math.acos(cos)
  //第三、四象限
  if (deltaY < 0) {
    a = -a
  }
  a = (a / Math.PI) * 180;
  return {distance, a}
}

/**
 * 把x小的点排到左边
 * @param p1
 * @param p2
 */
function sort(p1: EPoint, p2: EPoint) {
  if (p1.x > p2.x) {
    //转向
    return [p2, p1];
  } else {
    return [p1, p2];
  }
}

function sortY(p1: EPoint, p2: EPoint) {
  if (p1.y > p2.y) {
    //转向
    return [p2, p1];
  } else {
    return [p1, p2];
  }
}

function handleLineClick(event: MouseEvent, linkOption: LinkOption) {
  event.stopPropagation()
  emit('select', linkOption)
}

/**
 * 在折点上按下鼠标，开始处理拖动折点的功能
 * @param event
 * @param point
 * @param isStart
 * @param isEnd
 */
function handlePointMouseDown(event: MouseEvent, point: EPoint, isStart: boolean, isEnd: boolean) {
  event.preventDefault()
  event.stopPropagation()
  emit('dragLinePoint', event, props.option, point, isStart, isEnd)
}

function handleLineDbClick(event: MouseEvent, l: any, index: number) {
  event.preventDefault()
  event.stopPropagation()
  index = l.index
  let startPoint = new EPoint(l.x, l.y);
  let endPoint = new EPoint(l.x1, l.y1);
  // let a = sort(startPoint, endPoint);
  // startPoint = a[0]
  // endPoint = a[1]
  let r = (event.offsetX) / l.w / props.zoom
  let x: number = startPoint.x + r * (endPoint.x - startPoint.x)
  let y: number = startPoint.y + r * (endPoint.y - startPoint.y)
  props.option.points.splice(index + 1, 0, new EPoint(x, y))
  nextTick(() => {
    sync()
  })
}

function handlePointClick(event: MouseEvent, point: EPoint) {
  //双击一个折点，清除这个点
  event.stopPropagation();
  event.preventDefault();
  if (props.option.points.length < 3) {
    return;
  }
  let index = props.option.points.indexOf(point);
  if (index === 0) {
    props.option.startAnchor = null
  }
  if (index === props.option.points.length - 1) {
    props.option.endAnchor = null
  }
  props.option.points.splice(index, 1);
  nextTick(() => {
    sync()
  })
}
</script>
<style lang="scss" scoped>
.line {
  position: absolute;
  transform-origin: 0% 0%;
  box-sizing: border-box;
  cursor: pointer;
  z-index: 1;
  border-style: solid;
  border-left: 0;
  border-right: 0;

  &.selected {
    box-shadow: 0px 0px 5px #c9c9c9;
  }
}

.point {
  position: absolute;
  border-radius: 50%;
  width: 8px;
  height: 8px;
  box-sizing: border-box;
  background-color: #fff;
  border: 1px #000 solid;
  transform: translate(-50%, -50%);
  z-index: 2;
}

.circle {
  position: absolute;
  border-radius: 50%;
  box-sizing: border-box;
  transform: translate(-50%, -50%);
  border-style: solid;
}
</style>
